#include <ntddk.h>
#include <windef.h>

DWORD g_dwValue = 0;

VOID
DrvMultithreadUnload(PDRIVER_OBJECT DriverObject);

VOID
KStartRoutine(PVOID Context);

NTSTATUS
DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegisterPath)
{
	NTSTATUS Status            = STATUS_SUCCESS;
	DriverObject->DriverUnload = DrvMultithreadUnload;

	KMUTEX kMutex = { 0 };
	KeInitializeMutex(&kMutex, 0);

	HANDLE hThreads[2]     = { NULL };
	PVOID ThreadObjects[2] = { NULL };
	for (DWORD dwI = 0; dwI < 2; dwI++) {

		Status = PsCreateSystemThread(&hThreads[dwI], THREAD_ALL_ACCESS, NULL, NULL, NULL, KStartRoutine, &kMutex);
		if (NT_SUCCESS(Status) == FALSE) {

			KdPrintEx((77, 0, "PsCreateSystemThread_%d Error: %x\n", dwI, Status));
			return Status;
		}
 
		Status = ObReferenceObjectByHandle(hThreads[dwI], THREAD_ALL_ACCESS, *PsThreadType, KernelMode, &ThreadObjects[dwI], NULL);
		if (NT_SUCCESS(Status) == FALSE) {

			KdPrintEx((77, 0, "ObReferenceObjectByHandle_%d Error: %x\n", dwI, Status));
			ZwClose(hThreads[dwI]);
			return Status;
		}
	}
	
	Status = KeWaitForMultipleObjects(2, ThreadObjects, WaitAll, Executive, KernelMode, FALSE, NULL, NULL);
	if (NT_SUCCESS(Status) == FALSE) {

		KdPrintEx((77, 0, "KeWaitForMultipleObjects Error: %x\n", Status));
		
		for (DWORD dwI = 0; dwI < 2; dwI++) {

			ObDereferenceObject(ThreadObjects[dwI]);
			ZwClose(hThreads[dwI]);
		}

		return Status;
	}

	for (DWORD dwI = 0; dwI < 2; dwI++) {

		ObDereferenceObject(ThreadObjects[dwI]);
		ZwClose(hThreads[dwI]);
	}

	KdPrintEx((77, 0, "g_dwValue = %d\n", g_dwValue));

	return Status;
}

VOID
DrvMultithreadUnload(PDRIVER_OBJECT DriverObject)
{

}

VOID
KStartRoutine(PVOID Context)
{
	KeWaitForSingleObject((PKMUTEX)Context, Executive, KernelMode, FALSE, NULL);
	__try {

		for (DWORD dwI = 0; dwI < 1000000; dwI++) {
			g_dwValue += 1;
		}
	} __finally {

		KeReleaseMutex((PKMUTEX)Context, FALSE);
	}
}